package com.qianyitian.hope2.analyzer.service;

import com.alibaba.fastjson.JSON;
import com.qianyitian.hope2.analyzer.analyzer.MA30Analyzer;
import com.qianyitian.hope2.analyzer.model.KLineInfo;
import com.qianyitian.hope2.analyzer.model.RPS;
import com.qianyitian.hope2.analyzer.model.Stock;
import com.qianyitian.hope2.analyzer.model.SymbolList;
import com.qianyitian.hope2.analyzer.util.Utils;
import com.qianyitian.hope2.sdk.funds.FundBar;
import com.qianyitian.hope2.sdk.funds.FundInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class RPSService {
    public static final String STOCK_STORE_KEY = "stock-rps-hash";
    public static final String FUND_STORE_KEY = "fund-rps-hash";

    public static final String STOCK_REPORT_KEY_AGGR = "stock-rps-report-aggressive";
    public static final String STOCK_REPORT_KEY_STAB = "stock-rps-report-stable";
    public static final String FUND_REPORT_KEY_AGGR = "fund-rps-report-aggressive";
    public static final String FUND_REPORT_KEY_STAB = "fund-rps-report-stable";
    static double FundAggressiveValue = 90;
    static double FundStableValue = 70;
    static double STOCKAggressiveValue = 90;
    static double STOCKStableValue = 70;
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private StockService favoriteStockService;


    public int refreshStockRPS() {
        logger.info("start calculating each stock rps range");
        SymbolList symbols = favoriteStockService.getSymbols(null);

        for (Stock symbol : symbols.getSymbols()) {
            try {
                Stock stock = favoriteStockService.getStockDailySA(symbol.getCode());
                RPS rps = getRange4RPS(stock);
                storeEachStockRPS(symbol.getCode(), rps);
            } catch (Exception e) {
                logger.error(symbol.getCode() + "refresh stock rps error", e);
            }
        }
        logger.info("end calculating each stock rps range");

        logger.info("start calculating all stock rps");
        int size = calcStockRPS();
        logger.info("end calculating all stock rps");
        return size;
    }

    public String getFundRPS(String code) {
        String content = favoriteStockService.getFundDetail(code);
        if (content == null) {
            return null;
        }
        FundInfo fundInfo = JSON.parseObject(content, FundInfo.class);
        RPS rps = getRange4RPS(fundInfo);
        return JSON.toJSONString(rps);
    }

    public int refreshFundRPS() {
        logger.info("start calculating each fund rps range");
        List<Stock> funds = favoriteStockService.getSymbols("FUNDS").getSymbols();

        List<String> miniFunds = new LinkedList();
        for (Stock fund : funds) {
            try {
                String content = favoriteStockService.getFundDetail(fund.getCode());
                if (content == null) {
                    continue;
                }
                FundInfo fundInfo = JSON.parseObject(content, FundInfo.class);
                //不要太小的基金
                if (fundInfo.getScale() < 2) {
                    miniFunds.add(fundInfo.getCode());
                    continue;
                }
                RPS rps = getRange4RPS(fundInfo);
                storeEachFundRPS(fund.getCode(), rps);
            } catch (Exception e) {
                logger.error(fund.getCode() + "refresh fund rps error", e);
            }
        }
        logger.info("end calculating each fund rps range");
        logger.info("start calculating all fund rps");
        clearUselessFunds(miniFunds);
        int size = calcFundRPS();
        logger.info("end calculating all fund rps");
        return size;
    }

    MA30Analyzer ma30Analyzer = new MA30Analyzer();

    private RPS getRange4RPS(FundInfo fundInfo) {
        double range10 = calcRangeWithDays(fundInfo, 10);
        double range20 = calcRangeWithDays(fundInfo, 20);
        double range30 = calcRangeWithDays(fundInfo, 30);
        double range60 = calcRangeWithDays(fundInfo, 60);
        double range120 = calcRangeWithDays(fundInfo, 120);
        double range250 = calcRangeWithDays(fundInfo, 250);

        RPS rps = new RPS();
        rps.setCode(fundInfo.getCode());
        rps.setName(fundInfo.getName());
        rps.setRange10(range10);
        rps.setRange20(range20);
        rps.setRange30(range30);
        rps.setRange60(range60);
        rps.setRange120(range120);
        rps.setRange250(range250);

        boolean ok = ma30Analyzer.analyze(fundInfo);
        if (ok) {
            rps.setMa30Mark(ma30Analyzer.getRange());
        }
        return rps;


    }

    private void clearRedisSet() {
//        Cursor<String> cursor = redisTemplate.opsForSet().scan("rpsHash", ScanOptions.NONE);
//        while (cursor.hasNext()) {
//            String member = cursor.next();
//            redisTemplate.opsForSet().remove("rpsHash", member);
//        }
    }


    public RPS getRange4RPS(Stock stock) {
        double range10 = calcRangeWithDays(stock, 10);
        double range20 = calcRangeWithDays(stock, 20);
        double range30 = calcRangeWithDays(stock, 30);
        double range60 = calcRangeWithDays(stock, 60);
        double range120 = calcRangeWithDays(stock, 120);
        double range250 = calcRangeWithDays(stock, 250);

        RPS rps = new RPS();
        rps.setCode(stock.getCode());
        rps.setName(stock.getName());
        rps.setRange10(range10);
        rps.setRange20(range20);
        rps.setRange30(range30);
        rps.setRange60(range60);
        rps.setRange120(range120);
        rps.setRange250(range250);
        return rps;
    }

    public double calcRangeWithDays(Stock stock, int days) {
        if (stock.getkLineInfos().size() < days + 1) {
            return 0;
        }
        KLineInfo startK = stock.getkLineInfos().get(stock.getkLineInfos().size() - days - 1);
        KLineInfo endK = stock.getkLineInfos().get(stock.getkLineInfos().size() - 1);
        double range = Utils.calcRange(startK.getClose(), endK.getClose());
        return range;
    }

    public double calcRangeWithDays(FundInfo fundInfo, int days) {
        if (fundInfo.getFundBarList().size() < days + 1) {
            return 0;
        }
        FundBar startK = fundInfo.getFundBarList().get(fundInfo.getFundBarList().size() - days - 1);
        FundBar endK = fundInfo.getFundBarList().get(fundInfo.getFundBarList().size() - 1);
        double range = Utils.calcRange(startK.getValue(), endK.getValue());
        return range;
    }


    public void storeEachStockRPS(String code, RPS rps) {
        String s = JSON.toJSONString(rps);
        redisTemplate.opsForHash().put(STOCK_STORE_KEY, code, s);
//      redisTemplate.opsForSet().add("rpsHash", s);
    }

    public void storeEachFundRPS(String code, RPS rps) {
        String s = JSON.toJSONString(rps);
        redisTemplate.opsForHash().put(FUND_STORE_KEY, code, s);
    }

    public int calcStockRPS() {
        List list = redisTemplate.opsForHash().values(STOCK_STORE_KEY);

        List<RPS> rpsList = (List<RPS>) list.stream().map(item -> {
            String stock = (String) item;
            RPS rps = JSON.parseObject(stock, RPS.class);
            return rps;
        }).collect(Collectors.toList());

        calcRPS10(rpsList);
        calcRPS20(rpsList);
        calcRPS30(rpsList);
        calcRPS60(rpsList);
        calcRPS120(rpsList);
        calcRPS250(rpsList);
        {
            double value = STOCKAggressiveValue;
            List result = rpsList.stream().filter(item -> {
                return item.getRps10() > value || item.getRps20() > value || item.getRps30() > value
                        || item.getRps60() > value || item.getRps120() > value || item.getRps250() > value;
            }).collect(Collectors.toList());
            Map map = new HashMap();
            map.put("items", result);
            map.put("generateTime", LocalDateTime.now().toString());
            String content = JSON.toJSONString(map);
            redisTemplate.opsForValue().set(STOCK_REPORT_KEY_AGGR, content);
        }
        {
            double value = STOCKStableValue;
            List result = rpsList.stream().filter(item -> {
                return item.getRps10() > value && item.getRps20() > value && item.getRps30() > value
                        && item.getRps60() > value && item.getRps120() > value && item.getRps250() > value;
            }).collect(Collectors.toList());
            Map map = new HashMap();
            map.put("items", result);
            map.put("generateTime", LocalDateTime.now().toString());
            String content = JSON.toJSONString(map);
            redisTemplate.opsForValue().set(STOCK_REPORT_KEY_STAB, content);
        }
        return rpsList.size();
    }

    public void clearUselessFunds(List<String> uselessFunds) {
        for (String code : uselessFunds) {
            redisTemplate.opsForHash().delete(FUND_STORE_KEY, code);
        }
    }

    public int calcFundRPS() {
        List list = redisTemplate.opsForHash().values(FUND_STORE_KEY);

        List<RPS> rpsList = (List<RPS>) list.stream().map(item -> {
            String stock = (String) item;
            RPS rps = JSON.parseObject(stock, RPS.class);
            return rps;
        }).collect(Collectors.toList());

        calcRPS10(rpsList);
        calcRPS20(rpsList);
        calcRPS30(rpsList);
        calcRPS60(rpsList);
        calcRPS120(rpsList);
        calcRPS250(rpsList);
        int size = rpsList.size();

        {
            double value = FundAggressiveValue;
            List result = rpsList.stream().filter(item -> {
                return item.getRps10() > value || item.getRps20() > value || item.getRps30() > value || item.getRps60() > value || item.getRps120() > value || item.getRps250() > value;
            }).collect(Collectors.toList());
            Map map = new HashMap();
            map.put("items", result);
            map.put("generateTime", LocalDateTime.now().toString());
            String content = JSON.toJSONString(map);
            redisTemplate.opsForValue().set(FUND_REPORT_KEY_AGGR, content);
        }
        {
            double value = FundStableValue;
            rpsList = rpsList.stream().filter(item -> {
                return item.getRps10() > value && item.getRps20() > value && item.getRps30() > value && item.getRps60() > value && item.getRps120() > value && item.getRps250() > value;
            }).collect(Collectors.toList());
            Map map = new HashMap();
            map.put("items", rpsList);
            map.put("generateTime", LocalDateTime.now().toString());
            String content = JSON.toJSONString(map);
            redisTemplate.opsForValue().set(FUND_REPORT_KEY_STAB, content);
        }


        return size;
    }

    public String getAllStockRPSAggressive() {
        String content = redisTemplate.opsForValue().get(STOCK_REPORT_KEY_AGGR);
        return content;
    }

    public String getAllStockRPSStable() {
        String content = redisTemplate.opsForValue().get(STOCK_REPORT_KEY_STAB);
        return content;
    }

    public String getAllFundRPSAggressive() {
        String content = redisTemplate.opsForValue().get(FUND_REPORT_KEY_AGGR);
        return content;
    }

    public String getAllFundRPSSTable() {
        String content = redisTemplate.opsForValue().get(FUND_REPORT_KEY_STAB);
        return content;
    }

    private void calcRPS10(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange10(), o2.getRange10());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps10(score(i + 1, size));
        }
    }

    private double score(int index, int total) {
        return Utils.get2Double((double) index * 100 / total);
    }

    private void calcRPS20(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange20(), o2.getRange20());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps20(score(i + 1, size));
        }
    }

    private void calcRPS30(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange30(), o2.getRange30());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps30(score(i + 1, size));
        }
    }

    private void calcRPS60(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange60(), o2.getRange60());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps60(score(i + 1, size));
        }
    }

    private void calcRPS120(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange120(), o2.getRange120());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps120(score(i + 1, size));
        }
    }

    private void calcRPS250(List<RPS> rpsList) {
        int size = rpsList.size();
        rpsList.sort(new Comparator<RPS>() {
            @Override
            public int compare(RPS o1, RPS o2) {
                return Utils.compare(o1.getRange250(), o2.getRange250());
            }
        });
        for (int i = 0; i < rpsList.size(); i++) {
            RPS rps = rpsList.get(i);
            rps.setRps250(score(i + 1, size));
        }
    }
}
